#include <iostream>
#include <cassert>
#include "fmb.h"

using namespace std;
using namespace fm::base;

/*class A {
private:
  static int NUM_CTOR;
  static int NUM_COPY_CTOR;
  static int NUM_DTOR;
  static int NUM_ASIGMENT;
public:
  A() { ++NUM_CTOR; }
  A(const A& source) { ++NUM_COPY_CTOR; }
  A& operator=(const A& source) { ++NUM_ASIGMENT; return *this; }
  ~A() { ++NUM_DTOR; }

  static void test() {
    cout << "NUM_CTOR = " << NUM_CTOR << endl;
    cout << "NUM_COPY_CTOR = " << NUM_COPY_CTOR << endl;
    cout << "NUM_DTOR = " << NUM_DTOR << endl;
    cout << "NUM_ASIGMENT = " << NUM_ASIGMENT << endl;
    assert(NUM_CTOR + NUM_COPY_CTOR == NUM_DTOR);
    cout << "ok: NUM_CTOR + NUM_COPY_CTOR = NUM_DTOR" << endl;
  }
};

int A::NUM_CTOR = 0;
int A::NUM_COPY_CTOR = 0;
int A::NUM_DTOR = 0;
int A::NUM_ASIGMENT = 0;
*/
const int NUM_PUSHES = 10000;
Stack<int> stack1;
Stack<int> stack2;

void pushStack1() {
  for (int i = 0; i < NUM_PUSHES; ++i) {
    stack1.push(i);
    Thread::sleep(1);
  }
}

void pushStack2() {
  for (int i = 0; i < NUM_PUSHES; ++i) {
    stack2.push(i);
    Thread::sleep(1);
  }
}


void pop(Stack<int> &stack) {
  while (stack.num()) cout << stack.pop() << endl;
}

void compare(Stack<int> &st1, Stack<int> &st2) {
  assert(st1.num() == st2.num());
  while (st1.num()) assert(st1.pop() == st2.pop());
}


void compare(Stack<int> &st1, Stack<int> &st2, Stack<int> &st3) {
  assert(st1.num() == st2.num());
  assert(st1.num() == st3.num());
  while (st1.num()) {
    int h = st1.pop();
    assert(h == st2.pop());
    assert(h == st3.pop());
  }
}

int main(int argc, char **argv) {
  Thread t1(static_cast<Thread::RunFn>(&pushStack1));
  Thread t2(static_cast<Thread::RunFn>(&pushStack1));
  Thread t3(static_cast<Thread::RunFn>(&pushStack1));
  Thread t4(static_cast<Thread::RunFn>(&pushStack1));
  Thread t5(static_cast<Thread::RunFn>(&pushStack2));
  Thread t6(static_cast<Thread::RunFn>(&pushStack2));
  Thread::join(t1);
  Thread::join(t2);
  Thread::join(t3);
  Thread::join(t4);
  Thread::join(t5);
  Thread::join(t6);
  assert(stack1.num() == 4 * NUM_PUSHES);
  assert(stack2.num() == 2 * NUM_PUSHES);

  Stack<int> st1(stack1);
  Stack<int> st2 = stack1;
  compare(stack1, st1, st2);

  stack1 = stack2;
  compare(stack1, stack2);

  cout << "Stack test done" << endl;

  /*cout << "sizeof(A) = " << sizeof(A) << endl;
  {
    Stack<A> stack;
    for (int i = 0; i < 50; ++i) stack.push(A());
    while (stack.num() > 20) stack.pop();//cout << stack.pop() << endl;
    for (int i = 0; i < 50; ++i) stack.push(A());

    Stack<A> stack2(stack);
    assert(stack.num() == stack2.num());
    for (int i = 0; i < 10; ++i) stack2.pop();
    assert(stack.num() != stack2.num());
    Stack<A> stack3(stack);
    assert(stack.num() == stack3.num());
    stack3 = stack2;
    assert(stack2.num() == stack3.num());
  }

  A::test();
  */
  /*
  cout << "sizeof(int32) = " << sizeof(int32) << endl;
  cout << "sizeof(int64) = " << sizeof(int64) << endl;
  cout << "sizeof(uint32) = " << sizeof(uint32) << endl;
  cout << "sizeof(uint64) = " << sizeof(uint64) << endl;
  */
  return 0;
}